// Copyright (C) 2010, Scott W. Dunlop <swdunlop@gmail.com>
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//    * Neither the name of the <organization> nor the
//      names of its contributors may be used to endorse or promote products
//      derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
// ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <Python.h>
#include <structmember.h>
#include "module.h"
#include "nospex/sdl.h"

//--------------------------------------------- NoSpex Resource Type Metamethods 
static nx_graph_p nxpy_to_graph( PyObject* object );
static PyObject* nxpy_from_resource( nx_resource_p resource );

static void nxpy_fwd_event(
    void* ctxt, nx_resource_p rsrc, int id, double x, double y
){
    if( id == NX_ELEMENT_DRAG_EVT ){
        if( nx_is_node( rsrc ) ){
            nx_set_node_position( (nx_node_p)rsrc, x, y );
            nx_request_graph_physics( nx_resource_graph( rsrc ) );
        };
        return;
    };
    // We have no way of knowing if we are within our rights to call Python;
    // PyGILState_Ensure will secure those rights for us.
    PyGILState_STATE state = PyGILState_Ensure( ); 

    if( ctxt == NULL ) goto abort;

    PyObject* handle = nxpy_from_resource( rsrc );
    if( ! handle ) return; // TODO Not sure WHAT to do with exceptions, here..
    PyObject* arglist = Py_BuildValue( "(Oidd)", handle, id, x, y );
    if( ! arglist ){ PyErr_Print(); goto abort; };
    PyObject* result = PyObject_CallObject( (PyObject*) ctxt , arglist );
    if( ! result ) PyErr_Print();

    Py_DECREF(arglist);
    Py_DECREF(handle);
    Py_XDECREF(result);

abort:
    PyGILState_Release( state );
}

static PyObject* NoSpexResource_new( 
    PyTypeObject* type, PyObject* args, PyObject* kwds 
){
    NoSpexResource* self;
    self = (NoSpexResource*) type->tp_alloc( type, 0 );
    self->resource = NULL; 
    return (PyObject*)self;
}

static int NoSpexResource_traverse(
    NoSpexResource* self, visitproc visit, void* arg 
){
    
    nx_graph_p graph = nxpy_to_graph( (PyObject*)self );
    if( graph != NULL ){
        if( graph->event_cx != NULL ){
            visit( (PyObject*) graph->event_cx, arg );
        };
        goto done;
    };

done:
    
    return 0;
}

static int NoSpexResource_clear( NoSpexResource* self ){
    
    nx_remove_use( self->resource );
    self->resource = NULL;
    
    return 0;
}

static long NoSpexResource_hash( NoSpexResource* self ){
    return (long) self->resource;
}

static void NoSpexResource_dealloc( NoSpexResource* self ){
    NoSpexResource_clear( self );
    self->ob_type->tp_free((PyObject*)self);
}

static int NoSpexResource_compare( PyObject* left, PyObject* right );
static PyObject* NoSpexResource_repr( PyObject* self );

//------------------------------------------------------ NoSpexResource Utilities 
static nx_resource_p nxpy_to_resource( PyObject* object ){
    //TODO: Check object type.
    return ((NoSpexResource*)object)->resource;
};
static nx_element_p nxpy_to_element( PyObject* object ){
    nx_resource_p resource = nxpy_to_resource( object );
    if( resource == NULL ) return (nx_element_p) NULL;
    return nx_to_element( resource );
};
static nx_graph_p nxpy_to_graph( PyObject* object ){
    nx_resource_p resource = nxpy_to_resource( object );
    if( resource == NULL ) return (nx_graph_p) NULL;
    return nx_to_graph( resource );
};
static nx_node_p nxpy_to_node( PyObject* object ){
    nx_resource_p resource = nxpy_to_resource( object );
    if( resource == NULL ) return (nx_node_p) NULL;
    return nx_to_node( resource );
};
static nx_link_p nxpy_to_link( PyObject* object ){
    nx_resource_p resource = nxpy_to_resource( object );
    if( resource == NULL ) return (nx_link_p) NULL;
    return nx_to_link( resource );
};
/* UNUSED:
static nx_blip_p nxpy_to_blip( PyObject* object ){
    nx_resource_p resource = nxpy_to_resource( object );
    if( resource == NULL ) return (nx_blip_p) NULL;
    return nx_to_blip( resource );
};
static nx_info_p nxpy_to_info( PyObject* object ){
    nx_resource_p resource = nxpy_to_resource( object );
    if( resource == NULL ) return (nx_info_p) NULL;
    return nx_to_info( resource );
};
*/
//------------------------------------------------------ NoSpexResource Structure
static PyMethodDef NoSpexResourceMethods[] = {
    {NULL}
};

static PyMemberDef NoSpexResourceMembers[] = {
    {NULL}
};

static PyGetSetDef NoSpexResourceGetSet[] = {
    {NULL} 
};

static PyTypeObject NoSpexResourceType = {
    PyObject_HEAD_INIT( NULL )
    0,
    "NoSpexResource",
    sizeof( NoSpexResource ),
    0,                         /*tp_itemsize*/
    (destructor) NoSpexResource_dealloc,    /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    (cmpfunc) NoSpexResource_compare,    /*tp_compare*/
    (reprfunc) NoSpexResource_repr,       /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    (hashfunc) NoSpexResource_hash,       /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
    "Represents a NoSpex resource.",
    (traverseproc) NoSpexResource_traverse,   /* tp_traverse */
    (inquiry) NoSpexResource_clear,      /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    NoSpexResourceMethods,     /* tp_methods */
    NoSpexResourceMembers,     /* tp_members */
    NoSpexResourceGetSet,      /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    0,                         /* tp_init */
    0,                         /* tp_alloc */
    NoSpexResource_new,        /* tp_new *///TODO: Preventing new's.
};

static PyObject* nxpy_from_resource( nx_resource_p resource ){
    NoSpexResource* self;
    self = (NoSpexResource*)NoSpexResourceType.tp_alloc( &NoSpexResourceType, 0 );
    
    nx_add_use( resource );
    
    self->resource = resource;
    
    return (PyObject*)self;
};

static int NoSpexResource_compare( PyObject* left, PyObject* right ){
    if( left->ob_type != &NoSpexResourceType ){
        return +1;
    }else if( right->ob_type != &NoSpexResourceType ){
        return -1;
    };

    nx_resource_p l = ((NoSpexResource*)left)->resource;
    nx_resource_p r = ((NoSpexResource*)right)->resource;
    if( l == r ) return 0;
    
    int k = nx_resource_kind( l ) - nx_resource_kind( r );
    
    if( k < 0 ) return -1;
    if( k > 0 ) return +1;
    if( l > r ) return -1;
    if( r > l ) return +1; // Redundant..
    return 0;
};

static PyObject* NoSpexResource_repr( PyObject* self ){
    
    nx_resource_p rsrc = nxpy_to_resource( self );
    
    if( nx_is_graph( rsrc ) ) return PyString_FromFormat( 
        "<%s NoSpex Graph Resource %p>", 
        nx_to_graph( rsrc )->window ? "Visible" : "Invisible",
        rsrc 
    );

    char* kn; switch( nx_resource_kind( rsrc ) ){
    case NX_GRAPH_KIND: kn = "Graph"; break;
    case NX_INFO_KIND: kn = "Info"; break;
    case NX_NODE_KIND: kn = "Node"; break;
    case NX_LINK_KIND: kn = "Link"; break;
    case NX_BLIP_KIND: kn = "Blip"; break;
    default: kn = "Resource"; break;
    };
    nx_graph_p graph = nx_resource_graph( rsrc );
    

    return PyString_FromFormat( 
        "<%s %s Resource %p>", 
        graph ? "Linked" : "Unlinked",
        kn,
        rsrc
    );
}

#define BIND_CONSTANT( dict, name ) \
    PyDict_SetItemString( \
        dict, #name, (PyObject*) PyInt_FromLong( NX_##name ) \
    ); 

#define API_FUNC( name, doc ) \
    static PyObject* nxpy_##name( PyObject*, PyObject* ); \
    static PyMethodDef nxpy_md_##name = { \
        #name, (PyCFunction) nxpy_##name, METH_VARARGS, doc \
    }; static PyObject* nxpy_##name( PyObject* self, PyObject* args)

#define BIND_API( dict, name ) \
    PyDict_SetItemString(  \
        dict, #name, (PyObject*)PyCFunction_New( &nxpy_md_##name, NULL )  \
    ); 

#define RETURN_NONE \
    Py_INCREF(Py_None); \
    return Py_None;

#define RETURN_TRUE \
    Py_INCREF(Py_True ); \
    return Py_True;

#define RETURN_FALSE \
    Py_INCREF(Py_False ); \
    return Py_False;

#define RETURN_BOOL( x ) \
    if( x ){ RETURN_TRUE; }else{ RETURN_FALSE; };

// NOTE: DO NOT Decref arguments borrowed using ParseTuple!!
#define REQUIRE_RESOURCE( type, var, obj )  \
    nx_##type##_p var = nxpy_to_##type( obj ); \
    if( var == NULL ){ \
        PyErr_SetString( \
            PyExc_TypeError, #var " argument must be a resource of type " #type \
        ); goto failed; \
    }; 

#define API_CHECK_FUNC( type ) \
    API_FUNC( is_##type, "returns true if the supplied type is " #type ){ \
        PyObject* arg0 = NULL; \
        if( ! PyArg_ParseTuple(  \
            args, "O!:is_" #type, &NoSpexResourceType, &arg0 \
        ) ){ RETURN_FALSE; }; \
        \
        REQUIRE_RESOURCE( resource, resource, arg0 );  \
        RETURN_BOOL( nx_is_##type( resource ) ); \
    failed: \
        return NULL; \
    };
/*
API_FUNC( create_widget, "creates a NoSpex GTK widget" ){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:create_widget", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( graph, graph, arg0 ); 

    NxGraphWidget* widget = nx_create_widget( graph );
    return pygobject_new( G_OBJECT( widget ) );

failed:
    return NULL;
};
*/

API_FUNC( seize_control, "suspends the display thread" ){
    if( ! PyArg_ParseTuple( args, ":seize_control" ) ) return NULL;
    Py_BEGIN_ALLOW_THREADS;
    nx_seize_control( );
    Py_END_ALLOW_THREADS;
    RETURN_NONE;
};

API_FUNC( release_control, "resumes the display thread" ){
    if( ! PyArg_ParseTuple( args, ":release_control" ) ) return NULL;
    nx_release_control( );
    RETURN_NONE;
};

API_FUNC( display_graph, "uses SDL to create a display for the graph and loops" ){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:graph_display", &NoSpexResourceType, &arg0
    ) ) return NULL;
     
    REQUIRE_RESOURCE( graph, graph, arg0 ); 
    
    if( graph->window == NULL ) nx_create_window( graph );
    
    Py_BEGIN_ALLOW_THREADS;
    nx_window_loop( graph->window );
    Py_END_ALLOW_THREADS;
    RETURN_NONE;
failed:
    return NULL;
};

API_FUNC( 
    resource_graph, 
    "returns the graph associated with a resource or None if unlinked" 
){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:resource_graph", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( resource, resource, arg0 ); 
    
    nx_graph_p graph = nx_resource_graph( resource ); 
    
    if( graph ){
        return nxpy_from_resource( (nx_resource_p) graph ); 
    }else{
        RETURN_NONE;
    };
failed:
    return NULL;
};

API_FUNC( 
    resource_color, 
    "returns the RGB color associated with an resource as a tuple"
){
    PyObject* arg0 = NULL;
    double r, g, b;

    if( ! PyArg_ParseTuple( 
        args, "O!:resource_color", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( resource, resource, arg0 ); 
    nx_resource_color( resource, &r, &g, &b );
    return Py_BuildValue( "(ddd)", r, g, b );
failed:
    return NULL;
};

API_FUNC( 
    element_position, 
    "returns the x and y position associated with an element or 0.0, 0.0" 
){
    PyObject* arg0 = NULL;
    double x, y;

    if( ! PyArg_ParseTuple( 
        args, "O!:element_position", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( element, element, arg0 ); 
    
    if( ! nx_element_position( element, &x, &y ) ) x = y = 0.0;
    
    
    return Py_BuildValue( "(dd)", x, y );
failed:
    return NULL;
};

API_FUNC( 
    set_resource_color,
    "assigns the RGB color of a element, each commponent should be in [0.0,1.0)"
){
    PyObject* arg0 = NULL;
    double r, g, b;
    
    if( ! PyArg_ParseTuple( 
        args, "O!ddd:set_resource_color", &NoSpexResourceType, &arg0, &r, &g, &b
    ) ) return NULL;
    
    REQUIRE_RESOURCE( resource, resource, arg0 ); 
    
    nx_set_resource_color( (nx_resource_p) resource, r, g, b );

    RETURN_NONE;
failed:
    return NULL;
}

API_FUNC( 
    set_node_position, 
    "assigns the position of an node, x and y should be in the range [0.0,100.0)"
){
    PyObject* arg0 = NULL;
    double x, y;

    if( ! PyArg_ParseTuple( 
        args, "O!dd:set_node_position", &NoSpexResourceType, &arg0, &x, &y
    ) ) return NULL;
    
    REQUIRE_RESOURCE( node, node, arg0 ); 
    
    nx_set_node_position( node, x, y );
    
    
    RETURN_NONE;
failed:
    return NULL;
};

API_FUNC( 
    element_info, 
    "returns the info annotation associated with an element or None" 
){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:resource_graph", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( element, resource, arg0 ); 
    
    nx_info_p info = nx_element_info( resource ); 
    
    if( info ){
        return nxpy_from_resource( (nx_resource_p) info ); 
    }else{
        RETURN_NONE;
    };
failed:
    return NULL;
};

API_FUNC( 
    request_graph_physics, "requests a period of physics updates to a graph" 
){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:request_graph_physics", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( graph, graph, arg0 ); 
    
    nx_request_graph_physics( graph );
    
    RETURN_NONE;
failed:
    return NULL;
};

API_FUNC( 
    request_graph_blips, "requests a period of blips updates to a graph" 
){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:request_graph_blips", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( graph, graph, arg0 ); 
    
    nx_request_graph_blips( graph );
    
    RETURN_NONE;
failed:
    return NULL;
};

API_FUNC( 
    request_graph_render, "requests a redraw of the graph at Gtk's convenience"
){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:request_graph_render", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( graph, graph, arg0 ); 
    
    nx_request_graph_render( graph );
    
    RETURN_NONE;
failed:
    return NULL;
};

API_FUNC( create_graph, "creates a NoSpex graph" ){
    if( ! PyArg_ParseTuple( args, ":create_graph" ) ) return NULL;

    
    nx_graph_p graph = nx_create_graph( 100, 100 );
    
    return nxpy_from_resource( (nx_resource_p) graph );
};

API_FUNC( create_node, "creates a NoSpex node" ){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:create_node", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( graph, graph, arg0 ); 

    
    nx_node_p node = nx_create_node( graph );
    
    return nxpy_from_resource( (nx_resource_p) node );
failed:
    return NULL;
};

API_FUNC( create_link, "creates a NoSpex link" ){
    PyObject* arg0 = NULL;
    PyObject* arg1 = NULL;

    if( ! PyArg_ParseTuple( 
        args, "O!O!:create_link", 
        &NoSpexResourceType, &arg0, 
        &NoSpexResourceType, &arg1
    ) ) return NULL;
    
    REQUIRE_RESOURCE( node, src_node, arg0 ); 
    REQUIRE_RESOURCE( node, dest_node, arg1 ); 
    //TODO: Verify that nodes are graphed.
    //TODO: Verify that graph == graph.
    //TODO: Verify that src_node != dest_node.

    
    nx_link_p link = nx_create_link( src_node, dest_node );
    
    //TODO: if link is null, error.

    return nxpy_from_resource( (nx_resource_p) link );
failed:
    return NULL;
};

API_FUNC( create_blip, "creates a NoSpex blip" ){
    PyObject* arg0 = NULL;
    int arg1 = 0;
    int arg2 = 0;

    if( ! PyArg_ParseTuple( 
        args, "O!ii:create_blip", &NoSpexResourceType, &arg0, &arg1, &arg2
    ) ) return NULL;
    
    REQUIRE_RESOURCE( link, link, arg0 ); 
    //TODO: Verify that link is graphed.
    
    switch( arg1 ){
    case NX_FROM_SRC:
    case NX_FROM_DST:
        break;
    default:
        PyErr_SetString( 
            PyExc_TypeError, "origin must be either FROM_SRC or FROM_DST"
        ); goto failed;
    };
    
    if( arg2 <= 0 ){
        PyErr_SetString( 
            PyExc_TypeError, "duration must be a number of milliseconds > 0"
        ); goto failed;
    };

    
    nx_blip_p blip = nx_create_blip( link, arg1, arg2 );
    
    //TODO: if blip is null, error.

    return nxpy_from_resource( (nx_resource_p) blip );
failed:
    return NULL;
};

API_FUNC( create_info, "creates a NoSpex info annotation" ){
    PyObject* arg0 = NULL;
    char* arg1 = NULL;
    int len1 = 0;

    if( ! PyArg_ParseTuple( 
        args, "O!s#:create_info", 
        &NoSpexResourceType, &arg0, 
        &arg1, &len1
    ) ) return NULL;
    
    REQUIRE_RESOURCE( element, element, arg0 ); 
    //TODO: Verify that element is graphed.

    
    nx_info_p info = nx_create_info( element, arg1, len1 );
    
    //TODO: if link is null, error.

    return nxpy_from_resource( (nx_resource_p) info );
failed:
    return NULL;
};

API_FUNC( unlink_resource, "unlinks a NoSpex resource" ){
    PyObject* arg0 = NULL;
    if( ! PyArg_ParseTuple( 
        args, "O!:unlink_resource", &NoSpexResourceType, &arg0
    ) ) return NULL;
    
    REQUIRE_RESOURCE( resource, resource, arg0 ); 
    
    
    nx_unlink_resource( resource );
    
    RETURN_NONE;
failed:
    return NULL;
};

API_FUNC( set_graph_callback, "sets the NoSpex event callback" ){
    PyObject *arg0 = NULL;
    PyObject *arg1 = NULL;

    if( ! PyArg_ParseTuple(
        args, "O!O:set_graph_callback", &NoSpexResourceType, &arg0, &arg1
    )) return NULL;
    
    REQUIRE_RESOURCE( graph, graph, arg0 );    
    if( ! PyCallable_Check( arg1 ) ){
        PyErr_SetString(PyExc_TypeError, "callback argument must be callable");
        goto failed;
    }
    
    Py_XINCREF( arg1 );
    if( graph->event_fn == nxpy_fwd_event ){
        Py_XDECREF( graph->event_cx );  /* Dispose of previous callback */
    };
    
    nx_set_graph_callback( graph, arg1, nxpy_fwd_event );
    
    RETURN_NONE;
failed:
    return NULL;
}

API_CHECK_FUNC( graph );
API_CHECK_FUNC( node );
API_CHECK_FUNC( link );
API_CHECK_FUNC( blip );
API_CHECK_FUNC( info );

API_FUNC( is_element, "returns true if the supplied type is an element" ){ 
    PyObject* arg0 = NULL; 
    if( ! PyArg_ParseTuple( 
        args, "O!:is_element", &NoSpexResourceType, &arg0 
    ) ){ RETURN_FALSE; };
    
    REQUIRE_RESOURCE( resource, resource, arg0 );  
    RETURN_BOOL( nx_is_element( resource ) ); 
failed: 
    return NULL; 
};

const PyMethodDef nxpy_functions[] = { {NULL} };

//------------------------------------------------ NoSpex Module Initialization
DL_EXPORT( void ) initnxpy( void ){
    PyEval_InitThreads( );

    nx_start_system_clock( );
    nx_initialize_sdl( );

    PyObject *m, *d;
    
    NoSpexResourceType.tp_new = NoSpexResource_new;
    if( PyType_Ready( &NoSpexResourceType ) < 0 ) return;

    //init_pygobject( );
    m = Py_InitModule( "nxpy", (struct PyMethodDef*)nxpy_functions );
    d = PyModule_GetDict( m );
    // nxpy_register_classes( d );
    // if( PyErr_Occurred( ) ){
        // Py_FatalError( "could not initialize gtk portion of module nxpy" );
    // };

    BIND_API( d, display_graph );

    BIND_API( d, create_graph );
    BIND_API( d, create_node );
    BIND_API( d, create_link );
    BIND_API( d, create_blip );
    BIND_API( d, create_info );

    BIND_API( d, unlink_resource );
    BIND_API( d, set_graph_callback );

    BIND_API( d, is_graph );
    BIND_API( d, is_element );
    BIND_API( d, is_node );
    BIND_API( d, is_link );
    BIND_API( d, is_blip );
    BIND_API( d, is_info );

    BIND_API( d, resource_color );
    BIND_API( d, resource_graph );
    BIND_API( d, element_position );
    BIND_API( d, element_info );

    BIND_API( d, set_node_position );
    BIND_API( d, set_resource_color );

    BIND_API( d, request_graph_render );
    BIND_API( d, request_graph_blips );
    BIND_API( d, request_graph_physics );
    
    BIND_API( d, seize_control );
    BIND_API( d, release_control );

    BIND_CONSTANT( d, GRAPH_CLICK_EVT );
    BIND_CONSTANT( d, GRAPH_UNLINK_EVT );
    BIND_CONSTANT( d, ELEMENT_ENTER_EVT );
    BIND_CONSTANT( d, ELEMENT_LEAVE_EVT );
    BIND_CONSTANT( d, ELEMENT_CLICK_EVT );
    BIND_CONSTANT( d, ELEMENT_UNLINK_EVT );

    BIND_CONSTANT( d, FROM_SRC );
    BIND_CONSTANT( d, FROM_DST );

    Py_INCREF( &NoSpexResourceType );
    PyModule_AddObject( m, "NoSpexResource", (PyObject*) &NoSpexResourceType );
};

